package com.dpiinc.TechPage.activity

import android.graphics.Color
import android.media.MediaPlayer
import android.net.Uri
import android.os.Bundle
import android.os.Looper
import androidx.core.content.ContextCompat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import com.dpiinc.TechPage.BaseActivity
import com.dpiinc.TechPage.R
import com.dpiinc.TechPage.pen.activity.ReplayActivity.Companion.TIME_FORMAT_STR_SHORT
import com.dpiinc.TechPage.user.utils.ToastUtils
import com.dpiinc.TechPage.utils.DateUtils
import com.dpiinc.TechPage.widget.CustomPopWindow
import com.dpiinc.TechPage.widget.PlayerControlView
import com.beiji.lib.pen.PenLog
import com.beiji.lib.pen.cache.PenCacheManager
import com.beiji.lib.pen.constants.PaletteColor
import com.beiji.lib.pen.model.AudioRecord
import com.beiji.lib.pen.model.DotUnit
import com.beiji.lib.pen.model.PenStroke
import com.google.android.exoplayer2.ExoPlayerFactory
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import com.tqltech.tqlpencomm.bean.Dot
import kotlinx.android.synthetic.main.activity_play_audio.*
import kotlinx.coroutines.*
import kotlinx.coroutines.android.UI
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue

/**
 * 回放界面
 */
class PlayAudioActivity : BaseActivity() {

    companion object {
        const val TAG = "PlayAudioActivity"
        const val ARG_NOTE_KEY = "arg_note_key"
        const val REPLAY_DRAW_DELAY_PERIOD = 15L
    }

    private var strokeList = ArrayList<PenStroke>()
    private var audioRecordList = ArrayList<AudioRecord>()
    private var drawJob: Job? = null
    private var progressJob: Job? = null
    private var player: SimpleExoPlayer? = null
    private var curAudioRecord: AudioRecord? = null
    private var strokeToDrawQueue = ConcurrentLinkedQueue<PenStroke>()
    private var lastPosition: Long = 0

    private var audioListWindow: CustomPopWindow? = null
    private var curAudioIndex: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_play_audio)
        setSupportActionBar(toolbar)
        supportActionBar?.setDisplayShowTitleEnabled(false)
        toolbar.setNavigationOnClickListener { finish() }
        val noteId = intent.getStringExtra(ARG_NOTE_KEY)
        if (noteId.isNullOrEmpty()) {
            finish()
            return
        }
        if (!initData()) {
            finish()
            return
        }
        stroke_view.loadCacheAsBackground(noteId)
    }

    private fun initData(): Boolean {
        audioRecordList = PenCacheManager.instance.getCurrentCache()?.getAudioList()!!
        if (audioRecordList.isNotEmpty()) {
            curAudioRecord = audioRecordList[0]
        } else {
            ToastUtils.showCenterTip(this, getString(R.string.toast_no_audios))
            return false
        }
        curAudioRecord?.let {
            initPlayer(it)
        }
        initAudioListView()
        PenCacheManager.instance.getCurrentCache()?.readStrokesFromFile {
            strokeList = it
            processData(it)
        }
        return true
    }

    private fun initAudioListView() {
        if (audioRecordList.size == 1) {
            btn_audio_list.visibility = View.VISIBLE
            return
        }
        else if (audioRecordList.size < 1) {
            return
        }
        btn_audio_list.visibility = View.VISIBLE
        btn_audio_list.setOnClickListener {
            audioListWindow?.dismiss()
            audioListWindow = CustomPopWindow.PopupWindowBuilder(this)
                    .setView(getScView())
                    .size(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
                    .create()
                    .showAsDropDown(toolbar, true, 0)//AppUtil.dp2px(8f).toInt())
        }
    }

    private fun getScView():ScrollView{
        val scrollView = ScrollView(this)
        val audioListLayout = LinearLayout(this)
        audioListLayout.orientation = LinearLayout.VERTICAL
        audioListLayout.setBackgroundColor(Color.WHITE)
        scrollView.addView(audioListLayout)
        for (i in audioRecordList.indices) {
            val audioItem = LayoutInflater.from(this).inflate(R.layout.audio_file_item_layout, audioListLayout, false)
            audioItem.findViewById<TextView>(R.id.tv_audio_item).text = getString(R.string.audio_file_name, i + 1)
            audioItem.findViewById<TextView>(R.id.tv_audio_time).text = getRingDuring(audioRecordList[i].audioFile.absolutePath)
            val status: ImageView = audioItem.findViewById<ImageView>(R.id.iv_audio_stauts)
            val name: TextView = audioItem.findViewById<TextView>(R.id.tv_audio_item)
            val time: TextView = audioItem.findViewById<TextView>(R.id.tv_audio_time)
            if (i == curAudioIndex) {
                status.setImageResource(R.mipmap.icon_audio_play)
                name.setTextColor(ContextCompat.getColor(this, R.color.color_00b29a))
                time.setTextColor(ContextCompat.getColor(this, R.color.color_00b29a))
            }else{
                status.setImageResource(R.mipmap.icon_audio_stop)
                name.setTextColor(ContextCompat.getColor(this, R.color.pen_status_label_color))
                time.setTextColor(ContextCompat.getColor(this, R.color.color_505050))
            }
            if (i == audioRecordList.size - 1) {
                //audioItem.findViewById<View>(R.id.divider).visibility = View.GONE
            }
            audioItem.setTag(R.layout.audio_file_item_layout, i)
            audioItem.setOnClickListener(onAudioFileSelectedListener)
            audioListLayout.addView(audioItem)
        }
        return scrollView
    }

    private val onAudioFileSelectedListener = View.OnClickListener {
        audioListWindow?.dismiss()
        val position = it.getTag(R.layout.audio_file_item_layout) as Int
        if (position >= 0 && position < audioRecordList.size) {
            reset()
            curAudioIndex=position
            curAudioRecord = audioRecordList[position]
            initPlayer(audioRecordList[position])
            btn_audio_list.text = getString(R.string.audio_file_name, position + 1)
        }
    }

    private fun processData(strokeList: ArrayList<PenStroke>) {
        strokeToDrawQueue.clear()
        strokeList.forEach { stroke ->
            strokeToDrawQueue.offer(stroke)
        }
    }

    private fun reset() {
        player?.stop()
        progressJob?.cancel(null)
        drawJob?.cancel(null)
        lastPosition = 0
        processData(strokeList)
        stroke_view.clearAllStroke(false)
    }

    @Synchronized
    private fun offerStrokesIntoQueue(strokeList: ArrayList<PenStroke>) {
        strokeToDrawQueue.clear()
        strokeList.forEach { stroke ->
            //            if (stroke.action == PenStroke.ACTION_STROKE) {
            strokeToDrawQueue.offer(stroke)
//            }else if (stroke.action == PenStroke.ACTION_MARK){
//                strokeToDrawQueue.offer(stroke)
//            }
        }
    }

    private fun updateProgress() {
        drawJob?.cancel(null)
        progressJob?.cancel(null)
        progressJob = launch(CommonPool) {
            while (true) {
                val curPosition = player?.currentPosition ?: 0
                PenLog.i(TAG, "curPosition:$curPosition,lastPosition:$lastPosition")
                if (curPosition >= lastPosition) {
                    doDraw(curPosition, true, this)
                } else {
                    offerStrokesIntoQueue(strokeList)
                    stroke_view.clearAllStroke(false)
                    doDraw(curPosition, true, this)
                }
                lastPosition = curPosition
                delay(500)
            }
        }
    }

    private fun seekToPosition(positionMs: Long) {
        val targetPosition = if (positionMs < 0) 0 else positionMs
        progressJob?.cancel(null)
        drawJob?.cancel(null)
        drawJob = launch(UI) {
            if (targetPosition >= lastPosition) {
                doDraw(targetPosition, false, this)
            } else {
                offerStrokesIntoQueue(strokeList)
                stroke_view.clearAllStroke(false)
                doDraw(targetPosition, false, this)
            }
            lastPosition = targetPosition
        }
    }

    private suspend fun doDraw(curPosition: Long, playMode: Boolean, job: CoroutineScope? = null) {
        curAudioRecord ?: return
        val curTime = (curAudioRecord?.startTime ?: 0) + curPosition
        while (true) {
            if (job != null && !job.isActive) {
                break
            }
            val stroke = strokeToDrawQueue.peek()
            //当前播放位置之后的笔迹不播放
            if (stroke == null || stroke.time > curTime) {
                break
            }
            strokeToDrawQueue.remove(stroke)
            //在录音开始之前的笔迹不播放
            if (stroke.time < (curAudioRecord?.startTime ?: 0)) {
                continue
            }
            for (i in stroke.list.indices) {
                if (job != null && !job.isActive) {
                    break
                }
                val penPoint = stroke.list[i]
                val dot = DotUnit().apply {
                    pageId = stroke.page
                    bookId = stroke.bookId
                    actionType = stroke.action
                    timestamp = stroke.time
                    x = penPoint.x
                    y = penPoint.y
                    force = penPoint.f
                }
                dot.type = when (i) {
                    0 -> Dot.DotType.PEN_DOWN
                    stroke.list.indices.last -> Dot.DotType.PEN_UP
                    else -> Dot.DotType.PEN_MOVE
                }
//                if (isUIThread()) {
                if (stroke.action == PenStroke.ACTION_STROKE) {
                    stroke_view.setStrokeColor(PaletteColor.fromId(stroke.color))
                } else if (stroke.action == PenStroke.ACTION_MARK) {
                    stroke_view.setMarkColor(PaletteColor.fromId(stroke.color))
                }
                //TODO Yang
//                stroke_view.drawDot(dot, true)
                stroke_view.drawDot(dot)



//                } else {
//                    launch(UI) {
//                        stroke_view.setStrokeColor(PaletteColor.fromId(stroke.color))
//                        stroke_view.drawDot(dot)
//                    }
//                }
                if (playMode) {
                    delay(REPLAY_DRAW_DELAY_PERIOD)
                }
            }
        }
    }

    private fun isUIThread(): Boolean {
        return Thread.currentThread() == Looper.getMainLooper().thread
    }

    private fun initPlayer(audioRecord: AudioRecord) {
        val bandwidthMeter = DefaultBandwidthMeter()
        val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory(bandwidthMeter)
        val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
        // 2. Create the player
        player = ExoPlayerFactory.newSimpleInstance(applicationContext, trackSelector)

        // Measures bandwidth during playback. Can be null if not required.
        // Produces DataSource instances through which media data is loaded.
        val dataSourceFactory = DefaultDataSourceFactory(applicationContext,
                Util.getUserAgent(applicationContext, "AIWrite"), bandwidthMeter)
        // This is the MediaSource representing the media to be played.
        val audioUri = Uri.parse(audioRecord.audioFile.path)
        val videoSource = ExtractorMediaSource.Factory(dataSourceFactory)
                .createMediaSource(audioUri)
        // Prepare the player with the source.
        player?.prepare(videoSource)
        player?.addListener(playerEventListener)
        player_control_view.player = player
        player_control_view.showTimeoutMs = -1
        player_control_view.setControlDispatcher(controlDispatcher)
    }

    private val controlDispatcher = object : PlayerControlView.AiWriteControlDispatcher() {

        override fun dispatchSetPlayWhenReady(player: Player, playWhenReady: Boolean): Boolean {
            PenLog.e(TAG, "dispatchSetPlayWhenReady $playWhenReady")
            updateProgress()
            return super.dispatchSetPlayWhenReady(player, playWhenReady)
        }

        override fun dispatchSeeking(player: Player?, windowIndex: Int, positionMs: Long): Boolean {
//            PenLog.e(TAG, "dispatchSeeking -> $positionMs")
//            seekToPosition(positionMs)
            return super.dispatchSeeking(player, windowIndex, positionMs)
        }

        override fun dispatchSeekTo(player: Player, windowIndex: Int, positionMs: Long): Boolean {
            PenLog.e(TAG, "dispatchSeekTo-> positionMs:$positionMs , currentPosition:${player.currentPosition}")
            seekToPosition(positionMs)
            return super.dispatchSeekTo(player, windowIndex, positionMs)
        }

        override fun dispatchStop(player: Player, reset: Boolean): Boolean {
            PenLog.e(TAG, "dispatchStop")
            progressJob?.cancel(null)
            return super.dispatchStop(player, reset)
        }
    }

    private val playerEventListener: Player.DefaultEventListener = object : Player.DefaultEventListener() {
        override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
            PenLog.e(TAG, "onPlayerStateChanged->$playbackState")
            when (playbackState) {
                Player.STATE_READY -> {
                    if (playWhenReady) {
                        updateProgress()
                    }
                }
                Player.STATE_ENDED -> progressJob?.cancel(null)
            }
        }

    }

    fun getRingDuring(mUri: String?): String? {
        val mediaPlayer = MediaPlayer();
        mediaPlayer.setDataSource(mUri)
        mediaPlayer.prepare()
        mediaPlayer.getDuration()
        return DateUtils.getDurationString(mediaPlayer.getDuration().toLong(), TIME_FORMAT_STR_SHORT)
    }

    override fun onDestroy() {
        player?.stop()
        progressJob?.cancel(null)
        drawJob?.cancel(null)
        super.onDestroy()
    }
}
